使用 Systemd Timer 调度任务

Systemd 提供 timer 已经有一段时间了,作为 cron 的替代品,它值得一试。本篇文章将会告诉你如何使用 systemd 中的 timer 在系统启动后重复执行任务。这不是对 systemd 的详细讨论,只是对 timer 组件的介绍。

Cron vs anacron vs systemd:快速查看

Cron 可以以几分钟到几个月或更长时间的粒度运行。设置相对简单,只需要一个配置文件,尽管配置略微复杂,但是普通用户也能使用。

但是如果您的系统在任务的特定的时间点上不是开机状态,那么这次任务就会被跳过去

Anacron克服了“系统未运行”的问题。它确保当您的系统再次处于活动状态时将执行任务。虽然它旨在供管理员使用,但某些系统为一般用户提供访问权限。

但是,Anacron 的时间粒度最低是以天为单位。

cron 和 anacron 都有执行上下文必须一致的问题。任务运行时的环境和测试时的环境需要相同,必须提供相同的 shell、环境变量和路径。这意味着测试和调试有时很困难。

Systemd timer 具备 cron 和 anacron 的优秀能力,不仅允许调度粒度精细到分钟,而且允许错过时间点后执行任务。而且可供所有用户使用。

但是配置比较复杂,至少需要两个配置文件。

如果您的 cron 和 anacron 已经配置的很好了,那么可能没有理由去转换代码。但是 systemd 依然值得去研究,因为它可以简化 cron/anacron 的方案。

配置

Systemd timer 至少需要两个配置文件:定时器单元和服务单元。对于多行命令,你还需要一个“作业”文件或脚本。

定时器单元定义了调度方式,服务单元定义了执行的人物。有关定时器单元的详细信息,请参阅 systemd.timer ,对于服务单元的详细信息,参见 systemd.service

单元文件存在于多个位置(在手册页中列出)。然而,对于普通用户而言,最简单的位置是 ~/.config/systemd/user 。请注意:此处的 “user”是字符串“user”

演示

这个演示是一个简单的例子,它创建一个用户调度作业,而不是系统调度作业(以 root 身份运行)。它将消息、日期和时间打印到文件中。

  1. 首先创建一个执行任务的脚本,并将其存放到本地的“bin”目录:

    touch ~/bin/schedule-test.sh

    然后添加以下内容到您刚刚创建的文件中:

    #!/bin/sh
    echo "This is only a test: $(date)" >> "$HOME/schedule-test-output.txt"

    记得给予其执行权。

  2. 创建服务单元 ~/.config/systemd/user/schedule-test.service

    Description=A job to test the systemd scheduler
    
    Type=simple
    ExecStart=/home/<user>/bin/schedule-test.sh
    
    WantedBy=default.target

    请注意:<user> 应当是您的 @HOME 地址。但是单元文件路径名中的“user”就是字符串“user”

    ExecStart 应当提供的是一个没有变量的绝对地址。但对于用户单元,您可以使用“%h”替换 $HOME 。也就是说,您可以使用:

    ExecStart=%h/bin/schedule-test.sh

    这只能用于用户单元文件而不是系统单元,因为“%h”在系统环境中始终返回“/root”。其他替换可以在 systemd.unit 中的“SPECIFIERS”下找到。

  3. 创建一个定时器文件 ~/.config/systemd/user/schedule-test.timer ,它将调度您刚刚创建的服务单元: 。请注意,其与服务单元唯一的区别是拓展名不同。

    Description=Schedule a message every 1 minute
    RefuseManualStart=no  # Allow manual starts
    RefuseManualStop=no   # Allow manual stops
    
    #Execute job if it missed a run due to machine being off
    Persistent=true
    #Run 120 seconds after boot for the first time
    OnBootSec=120
    #Run every 1 minute thereafter
    OnUnitActiveSec=60
    #File describing job to execute
    Unit=schedule-test.service
    
    WantedBy=timers.target

    注意定时器文件使用“OnUnitActiveSec”指定调度。更灵活的方式是使用“OnCalendar”选项。例如:

    # run on the minute of every minute every hour of every day
    OnCalendar=*-*-* *:*:00
    # run on the hour of every hour of every day
    OnCalendar=*-*-* *:00:00
    # run every day
    OnCalendar=*-*-* 00:00:00
    # run 11:12:13 of the first or fifth day of any month of the year
    # 2012, but only if that day is a Thursday or Friday
    OnCalendar=Thu,Fri 2012-*-1,5 11:12:13

    更多“OnCalendar”相关的信息请看 这里

  4. 所有必要的工作都已经完成了,但是你应当首先测试一下。首先启用用户服务:

    $ systemctl --user enable schedule-test.service

    这将有以下输出:

    Created symlink /home/<user>/.config/systemd/user/default.target.wants/schedule-test.service → /home/<user>/.config/systemd/user/schedule-test.service.

    然后进行一次测试:

    $ systemctl --user start schedule-test.service

    检查您的输出文件($HOME/schedule-test-output.txt)确保你的脚本执行正确无误。

  5. 作业正常工作后,我们启用并启动用户计时器:

    $ systemctl --user enable --now schedule-test.timer

    注意:您在上述步骤中已经启用了服务,因此这里只需要启用定时器即可。

    这将产生类似下面的输出:

    Created symlink /home/<user>/.config/systemd/user/timers.target.wants/schedule-test.timer → /home/<user>/.config/systemd/user/schedule-test.timer.

其他操作

您可以检查和监控服务。如果您收到来自服务单元的错误,下面的第一个命令特别有用:

$ systemctl --user status schedule-test
$ systemctl --user list-unit-files

手动停止服务:

$ systemctl --user stop schedule-test.service

永久停止和禁用计时器和服务,重新加载守护程序配置并重置任何失败通知:

$ systemctl --user stop schedule-test.timer
$ systemctl --user disable schedule-test.timer
$ systemctl --user stop schedule-test.service
$ systemctl --user disable schedule-test.service
$ systemctl --user daemon -reload
$ systemctl --user reset-failed

概括

这篇文章将引导您使用systemd计时器,但是,systemd 的内容远不止这里介绍的内容。本文应该为您提供相关的基础。您可以从 Fedora 杂志 systemd 系列 开始探索更多相关信息 。

Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx